package com.spring.component.utils;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 线程池工具类
 * corePoolSize：核心线程数
 * maximumPoolSize：最大线程数
 * keepAliveTime：空闲线程存活时间
 * workQueue：缓存队列
 * 当任务数量不超过核心线程数时，线程池都会为该任务创建新的线程；
 * 当任务数量超过核心线程数但不超过缓存队列大小时，线程池不会创建新的线程；
 * 当任务数量超过核心线程数 + 缓存队列大小但不超过最大线程数，且核心线程无空闲，创建新的线程(最多为最大线程数)；
 * 当任务数量超过最大线程数，且无线程空闲时，抛出拒绝任务异常
 *
 * @author liushihua
 * @since 2018年04月04日
 */
@Component
public class ThreadPoolUtil {

    @Value("${thread.pool.corePoolSize:5}")
    private int corePoolSize;
    @Value("${thread.pool.maximumPoolSize:10}")
    private int maximumPoolSize;
    @Value("${thread.pool.keepAliveTime:200}")
    private long keepAliveTime;
    @Value("${thread.pool.workQueueSize:5}")
    private int workQueueSize;

    private ThreadPoolExecutor threadPoolExecutor;
    private Semaphore semaphore;

    /**
     * 返回同一个线程池
     *
     * @return
     */
    public ThreadPoolExecutor getExecutor() {
        if (null == threadPoolExecutor) {
            threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime,
                    TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(workQueueSize));
        }
        return this.threadPoolExecutor;
    }

    /**
     * 从信号量获取许可证，获得许可证，则将许可证数量减一，否则当前线程等待
     *
     * @throws InterruptedException
     */
    public void acquire() throws InterruptedException {
        getSemaphore().acquire();
    }

    /**
     * 释放许可证，将可用许可证的数量增加一个
     */
    public void release() {
        getSemaphore().release();
    }

    /**
     * 返回同一个信号量
     *
     * @return
     */
    public Semaphore getSemaphore() {
        if (null == semaphore) {
            semaphore = new Semaphore(maximumPoolSize + workQueueSize);
        }
        return this.semaphore;
    }

    @Override
    public String toString() {
        return "ThreadPoolUtil{" + "corePoolSize=" + corePoolSize + ", maximumPoolSize="
                + maximumPoolSize + ", keepAliveTime=" + keepAliveTime + ", workQueueSize=" + workQueueSize
                + '}';
    }
}
